package com.soc.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.soc.auth.constants.RedisConstants;
import com.soc.auth.domain.dto.CertificationDTO;
import com.soc.auth.domain.dto.StudentRegisterDTO;
import com.soc.auth.domain.dto.UserForgetDTO;
import com.soc.auth.domain.dto.UserUpdateDTO;
import com.soc.auth.domain.entity.Class;
import com.soc.auth.domain.entity.Roster;
import com.soc.auth.domain.entity.School;
import com.soc.auth.domain.entity.User;
import com.soc.auth.domain.vo.CourseUserVO;
import com.soc.auth.domain.vo.MetaVo;
import com.soc.auth.domain.vo.RouterVo;
import com.soc.auth.domain.vo.UserLoginVO;
import com.soc.auth.enumes.RoleEnum;
import com.soc.auth.exception.BusinessException;
import com.soc.auth.mapper.UserMapper;
import com.soc.auth.service.ClassService;
import com.soc.auth.service.RosterService;
import com.soc.auth.service.SchoolService;
import com.soc.auth.service.UserService;
import com.soc.auth.utils.CurrentUserUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author HongZhouAn
 * @since 2023-06-25
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final RedisTemplate<String, Object> redisTemplate;

    private final PasswordEncoder bCryptPasswordEncoder;

    private final RosterService rosterService;

    private final SchoolService schoolService;

    @Autowired
    @Lazy//解决循环依赖
    private ClassService classService;

    @Autowired
    @Lazy
    private UserMapper userMapper;





    @Override
    public List<RouterVo> getRouters() {

        User currentUser = CurrentUserUtil.getCurrentUser();
        if (RoleEnum.TEACHER.getCode() == currentUser.getRole()) {
            List<RouterVo> routers = new LinkedList<>();
            RouterVo routerVo = new RouterVo();
            routerVo.setName("Teacher");
            routerVo.setPath("index");
            routerVo.setHidden(false);
            routerVo.setRedirect("noRedirect");
            routerVo.setComponent("Layout");
            routerVo.setAlwaysShow(true);
            MetaVo metaVo = new MetaVo();
            metaVo.setTitle("老师端");
            metaVo.setIcon("system");
            metaVo.setNoCache(false);
            metaVo.setLink(null);
            routerVo.setMeta(metaVo);

            List<RouterVo> childrenRouters = new LinkedList<>();

            RouterVo childrenRouterVo1 = new RouterVo();
            childrenRouterVo1.setName("name");
            childrenRouterVo1.setPath("/login");
            childrenRouterVo1.setHidden(true);
            childrenRouterVo1.setComponent("/views/login");
            childrenRouters.add(childrenRouterVo1);

            RouterVo childrenRouterVo2 = new RouterVo();
            childrenRouterVo2.setName("create");
            childrenRouterVo2.setPath("/create");
            childrenRouterVo2.setHidden(true);
            childrenRouterVo2.setComponent("/views/course/courseCreate");
            childrenRouters.add(childrenRouterVo2);

            RouterVo childrenRouterVo3 = new RouterVo();
            childrenRouterVo3.setName("chapter");
            childrenRouterVo3.setPath("/chapter");
            childrenRouterVo3.setHidden(true);
            childrenRouterVo3.setComponent("/views/course/chapter");
            childrenRouters.add(childrenRouterVo3);

            RouterVo childrenRouterVo4 = new RouterVo();
            childrenRouterVo4.setPath("/create");
            childrenRouterVo4.setHidden(true);
            childrenRouterVo4.setComponent("/views/course/My");
            childrenRouters.add(childrenRouterVo4);

            RouterVo childrenRouterVo5 = new RouterVo();
            childrenRouterVo5.setPath("/mealDetail");
            childrenRouterVo5.setHidden(true);
            childrenRouterVo5.setComponent("/views/course/MealDetail");
            childrenRouters.add(childrenRouterVo5);

            RouterVo childrenRouterVo6 = new RouterVo();
            childrenRouterVo6.setPath("/mealCreate");
            childrenRouterVo6.setHidden(true);
            childrenRouterVo6.setComponent("/views/course/mealCreate");
            childrenRouters.add(childrenRouterVo6);

            RouterVo childrenRouterVo7 = new RouterVo();
            childrenRouterVo7.setPath("/ceshi");
            childrenRouterVo7.setHidden(true);
            childrenRouterVo7.setComponent("/views/course/ceshi");
            childrenRouters.add(childrenRouterVo7);

            RouterVo childrenRouterVo8 = new RouterVo();
            childrenRouterVo8.setPath("/ClassManage");
            childrenRouterVo8.setHidden(true);
            childrenRouterVo8.setComponent("/views/course/ClassManage");
            childrenRouters.add(childrenRouterVo8);

            RouterVo childrenRouterVo9 = new RouterVo();
            childrenRouterVo9.setPath("/class");
            childrenRouterVo9.setHidden(true);
            childrenRouterVo9.setComponent("/views/course/index2");
            childrenRouters.add(childrenRouterVo9);

            RouterVo childrenRouterVo10 = new RouterVo();
            childrenRouterVo10.setPath("/index2");
            childrenRouterVo10.setHidden(true);
            childrenRouterVo10.setComponent("/views/course/index");
            childrenRouters.add(childrenRouterVo10);

            RouterVo childrenRouterVo11 = new RouterVo();
            childrenRouterVo11.setPath("/classDetail");
            childrenRouterVo11.setHidden(true);
            childrenRouterVo11.setComponent("/views/course/classDetail");
            childrenRouters.add(childrenRouterVo11);

            RouterVo childrenRouterVo12 = new RouterVo();
            childrenRouterVo12.setPath("/chapterDetail");
            childrenRouterVo12.setHidden(true);
            childrenRouterVo12.setComponent("/views/course/chapterDetail");
            childrenRouters.add(childrenRouterVo12);

            RouterVo childrenRouterVo13 = new RouterVo();
            childrenRouterVo13.setName("publish");
            childrenRouterVo13.setPath("/publish");
            childrenRouterVo13.setHidden(true);
            childrenRouterVo13.setComponent("/views/course/publish");
            childrenRouters.add(childrenRouterVo13);

            RouterVo childrenRouterVo14 = new RouterVo();
            childrenRouterVo14.setPath("/mealcourse");
            childrenRouterVo14.setHidden(true);
            childrenRouterVo14.setComponent("/views/course/mealcourse");
            childrenRouters.add(childrenRouterVo14);

            RouterVo childrenRouterVo15 = new RouterVo();
            childrenRouterVo15.setName("SelectCourse");
            childrenRouterVo15.setPath("/SelectCourse");
            childrenRouterVo15.setHidden(true);
            childrenRouterVo15.setComponent("/views/course/SelectCourse");
            childrenRouters.add(childrenRouterVo15);

            RouterVo childrenRouterVo16 = new RouterVo();
            childrenRouterVo16.setPath("/404");
            childrenRouterVo16.setHidden(true);
            childrenRouterVo16.setComponent("/views/404");
            childrenRouters.add(childrenRouterVo16);

            RouterVo childrenRouterVo17 = new RouterVo();
            childrenRouterVo17.setPath("/complete");
            childrenRouterVo17.setHidden(true);
            childrenRouterVo17.setComponent("/views/course/Complete");
            childrenRouters.add(childrenRouterVo17);

            routerVo.setChildren(childrenRouters);
            routers.add(routerVo);

            System.out.println(routers);
            return routers;
        }
        if (RoleEnum.STUDENT.getCode() == currentUser.getRole()) {
            List<RouterVo> routers = new LinkedList<>();
            RouterVo routerVo = new RouterVo();
            routerVo.setName("Student");
            routerVo.setPath("index");
            routerVo.setHidden(false);
            routerVo.setRedirect("noRedirect");
            routerVo.setComponent("Layout");
            routerVo.setAlwaysShow(true);
            MetaVo metaVo = new MetaVo();
            metaVo.setTitle("学生端");
            metaVo.setIcon("system");
            metaVo.setNoCache(false);
            metaVo.setLink(null);
            routerVo.setMeta(metaVo);

            List<RouterVo> childrenRouters = new LinkedList<>();

            RouterVo childrenRouterVo1 = new RouterVo();
            childrenRouterVo1.setName("name");
            childrenRouterVo1.setPath("/stulogin");
            childrenRouterVo1.setHidden(true);
            childrenRouterVo1.setComponent("/views/login");
            childrenRouters.add(childrenRouterVo1);

            RouterVo childrenRouterVo2 = new RouterVo();
            childrenRouterVo2.setName("create");
            childrenRouterVo2.setPath("/stucreate");
            childrenRouterVo2.setHidden(true);
            childrenRouterVo2.setComponent("/views/student/courseCreate");
            childrenRouters.add(childrenRouterVo2);

            RouterVo childrenRouterVo3 = new RouterVo();
            childrenRouterVo3.setName("chapter");
            childrenRouterVo3.setPath("/stuchapter");
            childrenRouterVo3.setHidden(true);
            childrenRouterVo3.setComponent("/views/student/chapter");
            childrenRouters.add(childrenRouterVo3);

            RouterVo childrenRouterVo4 = new RouterVo();
            childrenRouterVo4.setPath("/stucreate");
            childrenRouterVo4.setHidden(true);
            childrenRouterVo4.setComponent("/views/student/My");
            childrenRouters.add(childrenRouterVo4);

            RouterVo childrenRouterVo5 = new RouterVo();
            childrenRouterVo5.setPath("/stumealDetail");
            childrenRouterVo5.setHidden(true);
            childrenRouterVo5.setComponent("/views/student/MealDetail");
            childrenRouters.add(childrenRouterVo5);

            RouterVo childrenRouterVo6 = new RouterVo();
            childrenRouterVo6.setPath("/stumealCreate");
            childrenRouterVo6.setHidden(true);
            childrenRouterVo6.setComponent("/views/student/mealCreate");
            childrenRouters.add(childrenRouterVo6);

            RouterVo childrenRouterVo7 = new RouterVo();
            childrenRouterVo7.setPath("/stuceshi");
            childrenRouterVo7.setHidden(true);
            childrenRouterVo7.setComponent("/views/student/ceshi");
            childrenRouters.add(childrenRouterVo7);

            RouterVo childrenRouterVo8 = new RouterVo();
            childrenRouterVo8.setPath("/stuClassManage");
            childrenRouterVo8.setHidden(true);
            childrenRouterVo8.setComponent("/views/student/ClassManage");
            childrenRouters.add(childrenRouterVo8);

            RouterVo childrenRouterVo9 = new RouterVo();
            childrenRouterVo9.setPath("/stuclass");
            childrenRouterVo9.setHidden(true);
            childrenRouterVo9.setComponent("/views/student/index2");
            childrenRouters.add(childrenRouterVo9);

            RouterVo childrenRouterVo10 = new RouterVo();
            childrenRouterVo10.setPath("/stuindex2");
            childrenRouterVo10.setHidden(true);
            childrenRouterVo10.setComponent("/views/student/index");
            childrenRouters.add(childrenRouterVo10);

            RouterVo childrenRouterVo11 = new RouterVo();
            childrenRouterVo11.setPath("/stuclassDetail");
            childrenRouterVo11.setHidden(true);
            childrenRouterVo11.setComponent("/views/student/classDetail");
            childrenRouters.add(childrenRouterVo11);

            RouterVo childrenRouterVo12 = new RouterVo();
            childrenRouterVo12.setPath("/stuchapterDetail");
            childrenRouterVo12.setHidden(true);
            childrenRouterVo12.setComponent("/views/student/chapterDetail");
            childrenRouters.add(childrenRouterVo12);

            RouterVo childrenRouterVo13 = new RouterVo();
            childrenRouterVo13.setName("publish");
            childrenRouterVo13.setPath("/stupublish");
            childrenRouterVo13.setHidden(true);
            childrenRouterVo13.setComponent("/views/student/publish");
            childrenRouters.add(childrenRouterVo13);

            RouterVo childrenRouterVo14 = new RouterVo();
            childrenRouterVo14.setPath("/stumealcourse");
            childrenRouterVo14.setHidden(true);
            childrenRouterVo14.setComponent("/views/student/mealcourse");
            childrenRouters.add(childrenRouterVo14);

            RouterVo childrenRouterVo15 = new RouterVo();
            childrenRouterVo15.setName("SelectCourse");
            childrenRouterVo15.setPath("/stuSelectCourse");
            childrenRouterVo15.setHidden(true);
            childrenRouterVo15.setComponent("/views/student/SelectCourse");
            childrenRouters.add(childrenRouterVo15);

            RouterVo childrenRouterVo16 = new RouterVo();
            childrenRouterVo16.setPath("/stu404");
            childrenRouterVo16.setHidden(true);
            childrenRouterVo16.setComponent("/views/404");
            childrenRouters.add(childrenRouterVo16);

            RouterVo childrenRouterVo17 = new RouterVo();
            childrenRouterVo17.setPath("/stucomplete");
            childrenRouterVo17.setHidden(true);
            childrenRouterVo17.setComponent("/views/student/Complete");
            childrenRouters.add(childrenRouterVo17);

            routerVo.setChildren(childrenRouters);
            routers.add(routerVo);

            return routers;
        }
        return null;
    }

    @Override
    public UserLoginVO getLoginSysUserInfo() {

        User currentUser = CurrentUserUtil.getCurrentUser();
        String currentUserId = currentUser.getId();

        User user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getId, currentUserId));
        UserLoginVO.SysUserInfo sysUserInfo = BeanUtil.copyProperties(user, UserLoginVO.SysUserInfo.class);
        sysUserInfo.setClassId(currentUser.getClassId());

        UserLoginVO userLoginVO = new UserLoginVO();
        userLoginVO.setUser(sysUserInfo);
        userLoginVO.setRoles(Collections.singletonList(RoleEnum.fromCode(user.getRole()).getMessage()));
        if (RoleEnum.TEACHER.getCode() == user.getRole()) {
            // 登录的用户是老师
            userLoginVO.setPermissions(Collections.singletonList("system:teacher:*"));
        } else if (RoleEnum.STUDENT.getCode() == user.getRole()){
            // 登录的用户是学生
            userLoginVO.setPermissions(Collections.singletonList("system:student:*"));
        } else {
            userLoginVO.setPermissions(Collections.singletonList("*:*:*"));
        }

        return userLoginVO;
    }

    /**
     * 学生进行注册
     * @param studentRegisterDTO 学校注册信息
     * @return 是否注册成功
     */
    @Override
    public boolean registerStudent(StudentRegisterDTO studentRegisterDTO) {

        String captcha = (String) redisTemplate.opsForValue().get(RedisConstants.CAPTCHA_KEY + studentRegisterDTO.getPhone());

        if (StrUtil.isBlank(captcha)) {
            throw new BusinessException("验证码已过期");
        }

        if (!studentRegisterDTO.getCaptcha().equals(captcha)) {
            throw new BusinessException("验证码输入错误");
        }

        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getPhone, studentRegisterDTO.getPhone());
        if (ObjectUtil.isNotNull(this.getOne(lambdaQueryWrapper))) {
            throw new BusinessException("手机号已经被注册过");
        }

        studentRegisterDTO.setPassword(bCryptPasswordEncoder.encode(studentRegisterDTO.getPassword()));
        User user = BeanUtil.copyProperties(studentRegisterDTO, User.class);
        user.setRole(RoleEnum.STUDENT.getCode());
        user.setCreateTime(new Date());
        user.setAvatar("https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0");
        return this.save(user);

    }

    /**
     * 修改当前登录用户信息
     * @param userUpdateDTO 要修改的信息
     * @return 修改是否成功
     */
    @Override
    public boolean updateCurrentUserInfo(UserUpdateDTO userUpdateDTO) {

        if (userUpdateDTO.getAvatar() != null && userUpdateDTO.getAvatar().length() == 0) {
            // 传过来的String类型的头像信息为空，则设为默认头像
            userUpdateDTO.setAvatar("https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0");
        }

        if (userUpdateDTO.getPassword() != null && userUpdateDTO.getPassword().length() == 0) {
            throw new BusinessException("密码不能为空");
        }

        String currentUserId = CurrentUserUtil.getCurrentUser().getId();

        if (StrUtil.isNotBlank(userUpdateDTO.getPassword())) {
            String newPassword = bCryptPasswordEncoder.encode(userUpdateDTO.getPassword());
            userUpdateDTO.setPassword(newPassword);
        }

        User user = BeanUtil.copyProperties(userUpdateDTO, User.class);
        user.setRealName(userUpdateDTO.getRealName());
        if (userUpdateDTO.getSchool() != null && userUpdateDTO.getSchool() != ""){
            LambdaQueryWrapper<School> schoolLambdaQueryWrapper = new LambdaQueryWrapper<>();
            schoolLambdaQueryWrapper.eq(School::getSchoolName,userUpdateDTO.getSchool());

            School schoolName = schoolService.getOne(schoolLambdaQueryWrapper);
            if (schoolName == null){
                throw new BusinessException("当前所填学校不存在");
            }
            user.setSchoolId(schoolName.getId());
        }

        user.setUpdateTime(new Date());
        user.setId(currentUserId);
        // 修改用户
        return this.updateById(user);
    }

    /**
     * 学生进行实名
     * @param certificationDTO 实名所需要的信息
     * @return 实名是否成功
     */
    @Transactional
    @Override
    public boolean certification(CertificationDTO certificationDTO) {
        String currentUserId = CurrentUserUtil.getCurrentUser().getId();
        User currentUser = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, currentUserId)
        );

        if (currentUser.getIsCertification() == 1) {
            throw new BusinessException("用户已经实名过");
        }

        // 通过班级绑定码获得该班级
        Class aClass = classService.getOne(new LambdaQueryWrapper<Class>()
                .eq(Class::getBindCode, certificationDTO.getBindCode())
        );

        if (ObjectUtil.isNull(aClass)) {
            throw new BusinessException("班级绑定码错误");
        }

        // 从数据库查询老师是否在aClass班级中导入该学生信息
        Roster roster = rosterService.getOne(new LambdaQueryWrapper<Roster>()
                .eq(Roster::getClassId, aClass.getId())
                .eq(Roster::getNo, certificationDTO.getNo())
                .eq(Roster::getRealName, certificationDTO.getRealName())
        );
        if (ObjectUtil.isNull(roster)) {
            throw new BusinessException("老师没有导入该学生的信息进入班级");
        }

        // 修改数据库的用户信息
        boolean result1 = this.update(new LambdaUpdateWrapper<User>()
                .set(User::getNo, certificationDTO.getNo())
                .set(User::getRealName, certificationDTO.getRealName())
                .set(User::getIsCertification, 1)
                .set(User::getClassId, aClass.getId())
                .set(User::getSchoolName, certificationDTO.getSchoolName())
                .eq(User::getId, currentUserId)
        );
        if (!result1) {
            throw new RuntimeException("操作数据库出现错误");
        }

        boolean result2 = rosterService.update(new LambdaUpdateWrapper<Roster>()
                .set(Roster::getIsCertification, 1)
                .eq(Roster::getNo, certificationDTO.getNo())
        );
        if (!result2) {
            throw new RuntimeException("操作数据库出现错误");
        }

        /**
         * 实名后置，操作course_user
         */
        //查到user_id,用于操作course_user
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getNo,certificationDTO.getNo());
        User getUserId = getOne(userLambdaQueryWrapper);

        if (ObjectUtil.isNull(getUserId) || StringUtils.isBlank(getUserId.getId())){
            throw new BusinessException("账号实名关联失败");
        }
        //通过班级码去查class_id,将信息添加
        List<String> courseId = userMapper.getCourseIdByClassId(aClass.getId());
        if (courseId.size() != 0){
            List<CourseUserVO> courseUserVOS = new ArrayList<>();

            for (String getCourseId : courseId) {
                CourseUserVO courseUserVO = new CourseUserVO();
                courseUserVO.setCourseUserId(RandomStringUtils.randomNumeric(20));
                courseUserVO.setUserId(getUserId.getId());
                courseUserVO.setCourseId(getCourseId);
                courseUserVO.setClassId(aClass.getId());
                courseUserVO.setType(0);
                courseUserVOS.add(courseUserVO);
            }

            userMapper.addCourseUserList(courseUserVOS);
        }
        return true;
    }

    @Override
    public boolean forgetPassword(UserForgetDTO userForgetDTO) {

        String captcha = (String) redisTemplate.opsForValue().get(RedisConstants.CAPTCHA_KEY + userForgetDTO.getPhone());

        if (StrUtil.isBlank(captcha)) {
            throw new BusinessException("验证码已过期");
        }

        if (!userForgetDTO.getValue().equals(captcha)) {
            throw new BusinessException("验证码输入错误");
        }

        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getPhone,userForgetDTO.getPhone());
        if (ObjectUtil.isNull(getOne(userLambdaQueryWrapper))){
            throw new BusinessException("当前手机号未注册，请先注册");
        }

        userForgetDTO.setPassword(bCryptPasswordEncoder.encode(userForgetDTO.getPassword()));
        LambdaQueryWrapper<User> userLqw = new LambdaQueryWrapper<>();
        userLqw.eq(User::getPhone,userForgetDTO.getPhone());
        User user = getOne(userLqw);

        if (user.getPassword().equals(userForgetDTO.getPassword())){
            throw new BusinessException("密码不能与修改前相同");
        }

        user.setPassword(userForgetDTO.getPassword());
        return updateById(user);

    }
}
